// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 ******************************************************************************
 * Copyright (C) 2007-2014, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */

package com.ibm.icu.impl.duration;

import java.util.Date;
import java.util.TimeZone;

/** Core implementation class for DurationFormatter. */
class BasicDurationFormatter implements DurationFormatter {
    private PeriodFormatter formatter;
    private PeriodBuilder builder;
    private DateFormatter fallback;
    private long fallbackLimit;
    private String localeName;
    private TimeZone timeZone;

    /**
     * Creates a basic duration formatter with the given formatter, builder, and fallback. It's up
     * to the caller to ensure that the locales and timezones of these are in sync.
     */
    public BasicDurationFormatter(
            PeriodFormatter formatter,
            PeriodBuilder builder,
            DateFormatter fallback,
            long fallbackLimit) {
        this.formatter = formatter;
        this.builder = builder;
        this.fallback = fallback;
        this.fallbackLimit = fallbackLimit < 0 ? 0 : fallbackLimit;
    }

    protected BasicDurationFormatter(
            PeriodFormatter formatter,
            PeriodBuilder builder,
            DateFormatter fallback,
            long fallbackLimit,
            String localeName,
            TimeZone timeZone) {
        this.formatter = formatter;
        this.builder = builder;
        this.fallback = fallback;
        this.fallbackLimit = fallbackLimit;
        this.localeName = localeName;
        this.timeZone = timeZone;
    }

    @Override
    public String formatDurationFromNowTo(Date targetDate) {
        long now = System.currentTimeMillis();
        long duration = targetDate.getTime() - now;
        return formatDurationFrom(duration, now);
    }

    @Override
    public String formatDurationFromNow(long duration) {
        return formatDurationFrom(duration, System.currentTimeMillis());
    }

    @Override
    public String formatDurationFrom(long duration, long referenceDate) {
        String s = doFallback(duration, referenceDate);
        if (s == null) {
            Period p = doBuild(duration, referenceDate);
            s = doFormat(p);
        }
        return s;
    }

    @Override
    public DurationFormatter withLocale(String locName) {
        if (!locName.equals(localeName)) {
            PeriodFormatter newFormatter = formatter.withLocale(locName);
            PeriodBuilder newBuilder = builder.withLocale(locName);
            DateFormatter newFallback = fallback == null ? null : fallback.withLocale(locName);
            return new BasicDurationFormatter(
                    newFormatter, newBuilder,
                    newFallback, fallbackLimit,
                    locName, timeZone);
        }
        return this;
    }

    @Override
    public DurationFormatter withTimeZone(TimeZone tz) {
        if (!tz.equals(timeZone)) {
            PeriodBuilder newBuilder = builder.withTimeZone(tz);
            DateFormatter newFallback = fallback == null ? null : fallback.withTimeZone(tz);
            return new BasicDurationFormatter(
                    formatter, newBuilder,
                    newFallback, fallbackLimit,
                    localeName, tz);
        }
        return this;
    }

    protected String doFallback(long duration, long referenceDate) {
        if (fallback != null && fallbackLimit > 0 && Math.abs(duration) >= fallbackLimit) {
            return fallback.format(referenceDate + duration);
        }
        return null;
    }

    protected Period doBuild(long duration, long referenceDate) {
        return builder.createWithReferenceDate(duration, referenceDate);
    }

    protected String doFormat(Period period) {
        if (!period.isSet()) {
            throw new IllegalArgumentException("period is not set");
        }
        return formatter.format(period);
    }
}
